#include <iostream>
#include <string>
#include <functional>
#include <stdlib.h>
#include <vector>
#include <unordered_map>
#include <ctime>

// 进程头文件
#include <unistd.h>

using work_t = std::function<void()>; // 返回值是void 参数为空
static int num;                       // 计数器 编号

//>>>>>状态栏编号
enum
{
    OK = 0,
    UsageRrror,
    PipeError,
    ForkError,
};

//>>>>>>任务
//>>>任务（1）
void Print_hw()
{
    std::cout << "hello world！" << std::endl;
}
//>>>任务（2）
void Print_nm()
{

    std::cout << "my name is" << std::endl;
}

//>>>>>任务管理
class Task
{
public:
    // 初始化任务
    Task()
    {
        srand((unsigned)time(nullptr));
        Insert(Print_hw);
        Insert(Print_nm);
    }
    // 插入任务
    void Insert(work_t t)
    {
        tasks[num++] = t;
    }
    // 选择任务 随机
    int Select()
    {
        unsigned _n = rand() % num;
        return _n;
    }
    // 销毁任务
    ~Task()
    {
    }
    // 执行对应的方法
    void Excute(int number)
    {
        if (tasks.find(number) == tasks.end())
            return;      // 没找到什么都不做
        tasks[number](); // 执行这个程序
    }

private:
    std::unordered_map<int, work_t> tasks;
};
Task _tm;

//>>>>>管道特点
class Channel
{
public:
    // 管道出事话
    Channel(int fd, int ffd) : _ffd(ffd), _fd(fd)
    {
        _name = std::to_string(ffd) + "->" + std::to_string(fd);
    }

    // 管道的销毁
    // ~Channel()
    // {
    // }

    // 打印管道特点
    std::string
    Name()
    {
        return _name;
    }

    // 用管道派发任务
    void Send(int _cmd) //_cmd 就是任务码
    {
        ::write(_fd, &_cmd, sizeof(_cmd)); // 把任务码发送到写端
    }

private:
    int _fd;    // 自己的pid 也就是自己的写端
    pid_t _ffd; // 父亲进程 谁创建了自己
    std::string _name;
};

//>>>>>子进程接收到信息后 Send()完毕之后 开始工作
void Work()
{
    while (true)
    {
        int _cmd = 0;
        int n = ::read(0, &_cmd, sizeof(_cmd)); // 因为dup2 重定向了
        if (n == sizeof(int))                   // 如果正好 在标准输入里面读取到一个字节 其实就是管道读到了 因为重定向了
        {
            _tm.Excute(_cmd);
        }
    }
}

//>>>>>加载进程
int progreeAll(const int &_num, std::vector<Channel> &_channels, work_t work)
{
    for (int i = 0; i < _num; i++)
    {
        // 创建管道
        int pipeArr[2] = {0};
        int _flag1 = pipe(pipeArr);
        if (_flag1 != 0)
        {
            return PipeError; // 表示管道创建失败了
        }

        // 管道创建成功
        // 创建进程
        pid_t id = fork();
        if (id < 0)
            return ForkError; // 进程创建失败
        if (id == 0)          // 说明进程 创建成功
        {
            ::close(pipeArr[1]); // 子进程关闭 写
            dup2(pipeArr[0], 0); // 重定向到
            work();
            ::exit(0);
        }
        ::close(pipeArr[0]); // 父进程 关闭读进程
        _channels.emplace_back();
    }
}

//>>>>>指令输入纠正
void Usage(std::string _s)
{
    std::cout << "_s" << "使用方式不对" << std::endl;
}

//>>>>>派发任务
void DispatchTask(std::vector<Channel> &_allchannel)
{
    int who = 0;
    int _num = 20;
    while (num--)
    {
        // a.选择一个任务，整数
        int _task = _tm.Select(); //_task 用来存 任务码

        // b.选择一个子进程的管道channel
        Channel &_channel = _allchannel[who++];

        who %= _allchannel.size();
        std::cout << "###################" << std::endl;
        // c.派发任务
        _channel.Send(_task); // 把任务派发给子进程的管道
        sleep(2);
    }
}

//>>>>>主函数
int main(int argc, char *argv[])
{

    // 首先输入指令
    if (argc != 2)
    {
        Usage(argv[0]);
        return UsageRrror;
    }
    int _num = std::stoi(argv[2]);
    std::vector<Channel> _allchannel; // 这个用来存放所有的管道

    // 1.输出进程池
    progreeAll(_num, _allchannel, Work);

    // 2.派发任务
    DispatchTask(_allchannel);

    // 3.退出进程池

    return 0;
}
